package com.yinxing.webapi.code.service.sys;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.yinxing.framework.mybatis.TemplateService;
import com.yinxing.framework.utils.AppUtils;
import com.yinxing.framework.utils.ServletUtils;
import com.yinxing.framework.utils.SpringUtils;
import com.yinxing.webapi.code.entity.sys.SysPermission;
import com.yinxing.webapi.code.entity.sys.SysPermissionQuery;
import com.yinxing.webapi.code.mapper.sys.SysPermissionMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional
public class ISysPermissionService extends TemplateService<SysPermission> implements InitializingBean {

    @Autowired
    private ISysRolepermissionService sysRolepermissionService;

    //google缓存工具
    private LoadingCache<String, SysPermission> cache;

    private SysPermissionMapper getMapper() {
        return (SysPermissionMapper) baseMapper;
    }

    /**
     * 数据库与代码反射集合同步
     * 将新开发的代码同步到库中
     */
    public void codeSync() {
        //通过spring获取全部controller
        Map<String, SysPermission> permissionMap = getControllerList();

        //将新增的方法写入到库中
        permissionMap.forEach((urlPattern, sysPermission) -> {
            SysPermission perm = selectByUrlPattern(urlPattern);
            if(perm != null) {
                perm.setUrlPattern_(urlPattern);
                perm.setControllerName_(sysPermission.getControllerName_());
                perm.setMethodName_(sysPermission.getMethodName_());
                perm.setHttpMethod_(sysPermission.getHttpMethod_());
                perm.setParametersType_(sysPermission.getParametersType_());
                perm.setLastSyncTime_(LocalDateTime.now());
                super.updateById(perm);
            } else {
                perm = new SysPermission();
                perm.setUrlPattern_(urlPattern);
                perm.setControllerName_(sysPermission.getControllerName_());
                perm.setMethodName_(sysPermission.getMethodName_());
                perm.setHttpMethod_(sysPermission.getHttpMethod_());
                perm.setParametersType_(sysPermission.getParametersType_());
                perm.setLastSyncTime_(LocalDateTime.now());
                //默认需要登录认证
                perm.setCheckLogin_(true);
                //其它默认都为false
                perm.setCheckPermission_(false);
                perm.setRequireLog_(false);
                perm.setCodeIDelete_(false);
                perm.setLogRequestParam_(false);
                perm.setLogReturnValue_(false);
                perm.setActionName_("");
                super.insert(perm);
            }
        });

        //将已经不存在的数据打上标记
        this.selectList().forEach(sysPermission -> {
            if(!permissionMap.containsKey(sysPermission.getUrlPattern_())) {
                sysPermission.setCodeIDelete_(true);
                super.updateById(sysPermission);
            }
        });

    }

    /**
     * 查询全部数据
     */
    public List<SysPermission> selectList() {
        QueryWrapper<SysPermission> qw = new QueryWrapper<>();
        return selectList(qw);
    }

    /**
     * 查询需要授权的数据列表
     * @param params 查询条件 (参数必须包含bindRole="true"|"false"和 sysRoleId)
     *               注意"true"和"false"必须为字符,因为mybatis动态if按字符串处理的.
     */
    public List<SysPermissionQuery> selectPermissionList(Map<String, Object> params) {
        AppUtils.notNull(params, "RoleBind_");
        AppUtils.notNull(params, "SysRoleId_");
        List<SysPermissionQuery> list = getMapper().selectPermissionList(params);
        if(params.get("RoleBind_").equals("true")) {
            //查询已经绑定过角色的权限 需要把RoleBind字段全部设置为true 这样前端ext表格全部为选中状态
            list.forEach(perm -> perm.setRoleBind_(true));
        }
        return list;
    }

    /**
     * 根据URL查询数据
     * @param urlPattern 请求路径
     */
    public SysPermission selectByUrlPattern(String urlPattern) {
        LambdaQueryWrapper<SysPermission> qw = new LambdaQueryWrapper<>();
        qw.eq(SysPermission::getUrlPattern_, urlPattern);
        return selectOne(qw);
    }

    /**
     * 获取程序全部Controller集合
     */
    public Map<String, SysPermission> getControllerList() {
        Map<String, SysPermission> permissionMap = new HashMap<>();

        Map<RequestMappingInfo, HandlerMethod> handlerMethods = SpringUtils.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> item : handlerMethods.entrySet()) {
            RequestMappingInfo mapping = item.getKey();
            HandlerMethod method = item.getValue();
            for (String urlPattern : mapping.getPatternsCondition().getPatterns()) {

                SysPermission perm = new SysPermission();
                perm.setUrlPattern_(urlPattern);
                perm.setControllerName_(method.getBeanType().getSimpleName());
                perm.setMethodName_(method.getMethod().getName());
                String httpMethods = ServletUtils.getHttpMethods(mapping.getMethodsCondition().getMethods());
                perm.setHttpMethod_(httpMethods);
                String paramTypes = AppUtils.getMethodParameterTypes(method.getMethodParameters());
                perm.setParametersType_(paramTypes);
                permissionMap.put(urlPattern, perm);

            }
        }

        return permissionMap;
    }

    /**
     * 批量更新数据
     * @param records
     */
    public void updateBatch(List<SysPermission> records) {
        records.forEach(permission -> {
            //当不需要权限验证时 删除角色权限绑定关系数据
            if(permission.getCheckPermission_() != null && !permission.getCheckPermission_()) {
                sysRolepermissionService.deleteByPermission(permission.getId_());
            }
            super.updateById(permission);
            //刷新缓存(一定要放在更新方法后面)
            this.refreshSysPermissionCacheById(permission.getId_());
        });
    }

    /**
     * 角色(绑定|解绑)权限
     * @param records: [{"RoleBind_":true,"Id_":"753341205303136266"},{"RoleBind_":true,"Id_":"753341225532264520"}]
     */
    public void updateRolePermission(long sysRoleId, List<SysPermissionQuery> records) {
        records.forEach(record->{
            if(record.getRoleBind_() != null && record.getRoleBind_()) {
                sysRolepermissionService.bindRolePermission(sysRoleId, record.getId_());
            } else {
                sysRolepermissionService.unBindRolePermission(sysRoleId, record.getId_());
            }
        });
    }

    /**
     * 删除数据同时删除全部角色权限绑定数据
     * @param id 主键PK
     */
    public void deleteMeAndAllPermById(Long id) {
        //删除角色权限绑定关系
        sysRolepermissionService.deleteByPermission(id);
        super.deleteById(id);
        //刷新缓存(一定要放在删除方法后面)
        this.refreshSysPermissionCacheById(id);
    }

    /**
     * 查询用户权限
     * @param sysUserId 用户ID
     */
    public Set<String> selectPermissionsBySysUserId(long sysUserId) {
        List<SysPermission> permissions = getMapper().selectPermissionsBySysUserId(sysUserId);
        return permissions.stream().map(permission ->
                permission.getUrlPattern_()).collect(Collectors.toSet());
    }

    /**
     * 删除数据同时删除全部角色权限绑定数据
     * @param ids id数组
     */
    public void deleteMeAndAllPermById(Long[] ids) {
        for (int i = 0; i < ids.length; i++) {
            this.deleteMeAndAllPermById(ids[i]);
        }
    }

    /**
     * spring启动后初始化方法
     */
    @Override
    public void afterPropertiesSet() {
        CacheLoader<String, SysPermission> loader = new CacheLoader<>() {
            @Override
            public SysPermission load(String key) {
                SysPermission perm = selectByUrlPattern(key);
                if (perm == null) {
                    //缓存未命中 创建一个空对象 防止缓存击穿
                    perm = new SysPermission();
                    perm.setUrlPattern_(key);
                    perm.setCheckLogin_(false);
                    perm.setCheckPermission_(false);
                    perm.setRequireLog_(false);
                }
                log.info("缓存未命中.从数据库查询权限数据 Key:{} | Value:{}", key, perm);
                return perm;
            }
        };

        this.cache = CacheBuilder.newBuilder()
                .expireAfterWrite(60, TimeUnit.MINUTES)
                .build(loader);
    }

    /**
     * 通过缓存获取权限数据
     * @param urlPattern 缓存key
     * @throws ExecutionException
     */
    public SysPermission getSysPermissionByCache(String urlPattern) throws ExecutionException {
        return cache.get(urlPattern);
    }

    /**
     * 刷新缓存，缓存会重新从DB加载
     * @param id 数据ID
     */
    public void refreshSysPermissionCacheById(long id) {
        SysPermission permission = selectById(id);
        if(permission != null) {
            cache.refresh(permission.getUrlPattern_());
        }
    }
}